Mutex(互斥鎖)、Condition Variables(條件變量)和Semaphore(信號量)是多線程編程中常用的同步原語,它們各自有不同的功能,但也可以相互配合使用。以下是它們之間的關係和主要區別:
互補性:這三者可以一起使用,以實現更複雜的同步需求。例如,可以使用互斥鎖來保護共享資源的訪問,使用條件變量來在某些條件下讓線程等待,使用信號量來控制同時訪問的線程數量。
典型用法:
以下是 Mutex
、Condition Variables
和 Semaphore
的獨立範例程式碼。
// mutex.cpp
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
int sharedResource = 0;
void increment() {
for (int i = 0; i < 10000; ++i) {
mtx.lock(); // 獲取鎖
++sharedResource; // 訪問共享資源
mtx.unlock(); // 釋放鎖
}
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << "Final value: " << sharedResource << std::endl;
// 因為兩個線程各自增加了 10000 次,所以是 20000。
return 0;
}
使用互斥鎖來保護共享資源,避免競爭條件,並使用兩個線程同時增加同一個變量的值。
// cv.cpp
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
std::queue<int> buffer;
std::mutex mtx;
std::condition_variable cv;
void producer() {
for (int i = 0; i < 10; ++i) {
{
std::lock_guard<std::mutex> lock(mtx);
buffer.push(i);
std::cout << "Produced: " << i << std::endl;
}
cv.notify_one(); // 通知消費者
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
void consumer() {
for (int i = 0; i < 10; ++i) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [] { return !buffer.empty(); }); // 等待條件變量
int item = buffer.front();
buffer.pop();
std::cout << "Consumed: " << item << std::endl;
}
}
int main() {
std::thread prodThread(producer);
std::thread consThread(consumer);
prodThread.join();
consThread.join();
return 0;
}
這段程式碼實現了一個使用條件變量的生產者-消費者模型。與之前的例子相比,這裡使用了 std::condition_variable
來更有效地處理生產者和消費者之間的同步。以下是程式碼的詳細解釋:
標頭檔:
#include <iostream>
:用於輸入輸出。#include <thread>
:用於多線程支持。#include <mutex>
:用於互斥鎖,保護共享資源。#include <condition_variable>
:用於條件變量,實現線程間的通知。#include <queue>
:用於佇列資料結構。全域變數:
std::queue<int> buffer;
:用於存儲生產的資料。std::mutex mtx;
:互斥鎖,用於保護對佇列的訪問。std::condition_variable cv;
:條件變量,用於通知消費者有資料可消費。producer():
std::lock_guard<std::mutex> lock(mtx);
獲取鎖,然後將資料放入佇列。cv.notify_one();
通知消費者有新資料可用。consumer():
std::unique_lock<std::mutex> lock(mtx);
獲取鎖,然後使用 cv.wait(lock, [] { return !buffer.empty(); });
等待條件變量,直到佇列不再空。cv.wait(lock, [] { return !buffer.empty(); });
是一個條件等待,只有當 buffer
非空時,消費者才會從等待中醒來。這段程式碼展示了如何使用 C++ 的多線程功能和條件變量來實現一個高效的生產者-消費者模型。生產者和消費者之間的協調通過條件變量來實現,這樣可以提高資源的使用效率。
// semaphore.cpp
#include <iostream>
#include <thread>
#include <semaphore.h>
#include <queue>
#include <mutex>
#include <chrono>
const int MAX_QUEUE_SIZE = 5;
std::queue<int> buffer;
std::mutex mtx;
sem_t empty; // 用於表示空位的信號量
sem_t full; // 用於表示佇列中項目的信號量
void producer() {
std::cout << "Producer starting..." << std::endl;
for (int i = 0; i < 10; ++i) {
sem_wait(&empty); // 等待有空位
{
std::lock_guard<std::mutex> lock(mtx);
buffer.push(i);
std::cout << "Produced: " << i << std::endl;
}
sem_post(&full); // 增加佇列中項目的計數
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
void consumer() {
std::cout << "Consumer starting..." << std::endl;
for (int i = 0; i < 10; ++i) {
sem_wait(&full); // 等待有項目可消費
{
std::lock_guard<std::mutex> lock(mtx);
int item = buffer.front();
buffer.pop();
std::cout << "Consumed: " << item << std::endl;
}
sem_post(&empty); // 增加空位的計數
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
}
int main() {
sem_init(&empty, 0, MAX_QUEUE_SIZE); // 初始化空位信號量
sem_init(&full, 0, 0); // 初始化佇列中項目的信號量為0
std::thread prodThread(producer);
std::thread consThread(consumer);
prodThread.join();
consThread.join();
sem_destroy(&empty); // 銷毀信號量
sem_destroy(&full); // 銷毀信號量
return 0;
}
這段程式碼實現了一個簡單的生產者-消費者模型,使用 C++ 的多線程功能來模擬生產者生成資料並將其放入佇列,消費者則從佇列中取出資料進行消費。以下是程式碼的詳細說明:
標頭檔:
#include <iostream>
:用於輸入輸出。#include <thread>
:用於多線程支持。#include <semaphore.h>
:用於信號量的操作。#include <queue>
:用於佇列資料結構。#include <mutex>
:用於互斥鎖,保護共享資源。#include <chrono>
:用於時間相關的功能。全域變數:
const int MAX_QUEUE_SIZE = 5;
:佇列的最大容量。std::queue<int> buffer;
:用於存儲生產的資料。std::mutex mtx;
:互斥鎖,用於保護對佇列的訪問。sem_t empty;
:信號量,用於表示佇列中的空位。sem_t full;
:信號量,用於表示佇列中已有的項目。producer():
sem_wait(&empty);
等待有空位。std::lock_guard<std::mutex> lock(mtx);
獲取鎖,然後將資料放入佇列。sem_post(&full);
增加佇列中項目的計數。consumer():
sem_wait(&full);
等待有項目可消費。std::lock_guard<std::mutex> lock(mtx);
獲取鎖,然後從佇列中取出資料。sem_post(&empty);
增加空位的計數。main():
sem_init(&empty, 0, MAX_QUEUE_SIZE);
:設定空位信號量的初始值為最大佇列容量。sem_init(&full, 0, 0);
:設定佇列中項目的信號量初始值為 0。std::thread prodThread(producer);
std::thread consThread(consumer);
join()
等待兩個執行緒完成。std::this_thread::sleep_for
來模擬生產和消費的延遲,這樣可以更清楚地看到生產和消費的過程。這段程式碼是一個經典的生產者-消費者問題的實現,展示了如何使用多線程和信號量來解決資源共享的問題。
Mutex 範例:這個範例展示了如何使用 Mutex
來保護對共享資源的訪問,確保兩個線程同時增量共享變量不會導致競爭條件。
Condition Variables 範例:這個範例展示了生產者和消費者之間的協作,使用條件變量來等待和通知。
Semaphore 範例:這個範例展示了如何使用信號量來限制同時訪問的資源數量,確保不會超過緩衝區的容量。